package deepbrain.simnetwork.structure;

import java.io.Serializable;

import deepbrain.simnetwork.exception.SimNetworkException;

/**
 * Data structure matrix which represents the connections of networks, which can
 * be serialized to and parsed from String.
 * 
 * @author Li Yang
 * @create 2009-2-21
 */
public abstract class Matrix<WeightType> implements Serializable {

	private static final long serialVersionUID = -8774736747287050860L;

	protected WeightType[][] matrix;
	protected int numOfRows;
	protected int numOfCols;
	protected String name;

	public Matrix() {
		this.numOfRows = 0;
		this.numOfCols = 0;
	}

	public Matrix(String parseLine) throws SimNetworkException {
		ParseFromString(parseLine);
	}

	public Matrix(Object[][] matrix) throws SimNetworkException {
		setMatrix(matrix);
	}

	public Object[][] getMatrix() {
		return matrix;
	}

	public WeightType getElement(int y, int x) {
		return matrix[y][x];
	}

	public void setElement(int y, int x, WeightType value) {
		matrix[y][x] = value;
	}

	@SuppressWarnings("unchecked")
	public void setMatrix(Object[][] matrix) throws SimNetworkException {
		boolean validateResult = validateMatrix(matrix);
		if (!validateResult)
			throw new SimNetworkException("Matrix: \n" + getMatrixAsString()
					+ "\nIs NOT a " + getMatrixType());
		this.matrix = (WeightType[][]) matrix;
		if (matrix.length == 0) {
			numOfRows = 0;
			numOfCols = 0;
		} else {
			numOfRows = matrix.length;
			numOfCols = matrix[0].length;
		}
	}

	public String getName() {
		if (name == null)
			return "";
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getNumOfRows() {
		return numOfRows;
	}

	public int getNumOfCols() {
		return numOfCols;
	}

	protected boolean validateMatrix(Object[][] matrix) {
		return MatrixUtils.validateMatrix(matrix);
	}

	protected String getMatrixAsString() {
		return MatrixUtils.getMatrixAsString(this);
	}

	public String getMatrixType() {
		return "Matrix";
	}

	public WeightType translateToWeightTypeExceptionNest(String weight)
			throws SimNetworkException {
		try {
			return parseWeight(weight);
		} catch (RuntimeException e) {
			throw new SimNetworkException(
					"Cannot Parse the Weight Type of Weight: " + weight, e);
		}
	}

	@Override
	public String toString() {
		return "Matrix [" + getName() + "]: \n" + getMatrixAsString();
	}

	public abstract WeightType parseWeight(String weight)
			throws SimNetworkException;

	public String serializeWeight(WeightType weight) {
		return weight.toString();
	}

	public String SerializeToString() {
		StringBuilder builder = new StringBuilder(numOfRows * numOfCols * 2);
		int i = 0, j = 0;
		builder.append("[");
		for (i = 0; i < numOfRows; i++) {
			builder.append("[");
			for (j = 0; j < numOfCols - 1; j++) {
				builder.append(serializeWeight(matrix[i][j]));
				builder.append(", ");
			}
			builder.append(serializeWeight(matrix[i][j]));
			builder.append("]");
			if (i < numOfRows - 1)
				builder.append(", ");
		}
		builder.append("]");
		return builder.toString();
	}
	
	public String SerializeToMatlabString() {
		StringBuilder builder = new StringBuilder(numOfRows * numOfCols * 2);
		int i = 0, j = 0;
		for (i = 0; i < numOfRows; i++) {
			for (j = 0; j < numOfCols - 1; j++) {
				builder.append(serializeWeight(matrix[i][j]));
				builder.append(" ");
			}
			builder.append(serializeWeight(matrix[i][j]));
			builder.append("\n");
		}
		return builder.toString();
	}

	@SuppressWarnings("unchecked")
	public void ParseFromString(String line) throws SimNetworkException {
		String[] pieces = line.trim().split("], ");
		matrix = (WeightType[][]) new Object[pieces.length][];
		for (int i = 0; i < pieces.length; i++) {
			String partStr = pieces[i];
			while (partStr.startsWith("["))
				partStr = partStr.substring(1);
			while (partStr.endsWith("]"))
				partStr = partStr.substring(0, partStr.length() - 1);
			while (partStr.endsWith(", "))
				partStr = partStr.substring(0, partStr.length() - 2);
			String[] elements = partStr.split(", ");
			matrix[i] = (WeightType[]) new Object[elements.length];
			for (int j = 0; j < elements.length; j++)
				matrix[i][j] = parseWeight(elements[j]);

		}
	}

}
